package com.caiheng.api.util;

/**
 * @创建者：zhouwei
 * @创建时间：2022/5/9
 * @描述：
 */

import java.util.HashMap;
import java.util.Map;

/**
 * 使用最小二乘法实现线性回归预测
 *
 * @author daijiyong
 */
public class LinearRegression {
    /**
     * 训练集数据
     */
    private Map<Double, Double> initData = new HashMap<>();
    /**
     * 截距
     */
    private double intercept = 0.0;
    //斜率
    private double slope = 0.0;
    /**
     * x、y平均值
     */
    private double averageX = 0.0, averageY = 0.0;
    /**
     * 求斜率的上下两个分式的值
     */
    private double slopeUp = 0.0, slopeDown = 0.0;
    public LinearRegression(Map<Double, Double> initData) {
        this.initData = initData;
        initData();
    }
    public LinearRegression() {
    }
    /**
     * 根据训练集数据进行训练预测
     * 并计算斜率和截距
     */
    public void initData() {
        if (initData.size() > 0) {
            //数据个数
            int number = 0;
            //x值、y值总和
            double sumX = 0;
            double sumY = 0;
            averageX = 0;
            averageY = 0;
            slopeUp = 0;
            slopeDown = 0;
            for (Double x : initData.keySet()) {
                if (x == null || initData.get(x) == null) {
                    continue;
                }
                number++;
                sumX = DoubleUtils.add(sumX, x);
                sumY = DoubleUtils.add(sumY, initData.get(x));
            }
            //求x，y平均值
            averageX = DoubleUtils.div(sumX, (double) number);
            averageY = DoubleUtils.div(sumY, (double) number);
            for (Double x : initData.keySet()) {
                if (x == null || initData.get(x) == null) {
                    continue;
                }
                slopeUp = DoubleUtils.add(slopeUp, DoubleUtils.mul(DoubleUtils.sub(x, averageX), DoubleUtils.sub(initData.get(x), averageY)));
                slopeDown = DoubleUtils.add(slopeDown, DoubleUtils.mul(DoubleUtils.sub(x, averageX), DoubleUtils.sub(x, averageX)));
            }
            initSlopeIntercept();
        }
    }
    /**
     * 计算斜率和截距
     */
    private void initSlopeIntercept() {
        if (slopeUp != 0 && slopeDown != 0) {
            slope = slopeUp / slopeDown;
        }
        System.out.println("斜率: " + slope);

        intercept = averageY - averageX * slope;
        System.out.println("截距: " + intercept);
    }
    /**
     * 根据x值预测y值
     *
     * @param x x值
     * @return y值
     */
    public Double getY(Double x) {
        return DoubleUtils.add(intercept, DoubleUtils.mul(slope, x));
    }
    /**
     * 根据y值预测x值
     *
     * @param y y值
     * @return x值
     */
    public Double getX(Double y) {
        return DoubleUtils.div(DoubleUtils.sub(y, intercept), slope);
    }
    public Map<Double, Double> getInitData() {
        return initData;
    }
    public void setInitData(Map<Double, Double> initData) {
        this.initData = initData;
    }

    public static void main(String[] args) {
        LinearRegression linearRegression = new LinearRegression();

        double[] data = {
                265.9,269,270.4,270,272.2,272.1,272.5,266.9,271.4,268.8,263.2,268,266.3,267.4,267.7,266.6,271.8,
                270.7,270,273.3,270.4,269.5,270.8,272.7,273.7,274.5,269.8,272.8,271.8,275.3,273.6,271.4,271.1,272.2,
                276.2,266.7,273.2,271.1,271.1,272.2,272.1,274,268,268.5,274,270.8,268.4,272.9,268,273,270,268.5,273.2,
                274.4,271.4,268,272.5,271.9,275.5,272.3,271.8,270.8,271.8,269.3,271.7,266.9,267.7,266.1,272.1,273.3,
                277.3,277.3,275.2,275.8,273.9,276.2,274.8,273.7,274,276.9,276.9,277.2,273.5,273.1,274.3,278,272.8,275.8,
                276.1,280,276.5,278.3,277.7,280.6,281.1,280.4,277.7,275.9,279.5,277.6,276.5,283.8,282.4,283.4,284.4,282.1,
                285.8,284.3,277.7,284.3,279.5,282.8,286.5,280.9,285.9,283.5,283.9,283.2,284,280.6,288.2,285.8,271.9,261.9,
                248.4,234.3,224.7,228.8,215.5,206.6,196.6,189.9,187.1,170.9,167.2,165.2,158.3,151.2,147.3,142,137.8,131.2,126.4,
                122,115.6,112.4,108.2,104.5,102.3,98.1,94.7,92.7,89.5,86.8,84.1,82.5,78.3,76.9,74.6,72.5,70.3,66.9,66.4,63,63.6,
                61.8,60.3,59,55.7,55,53.7,52.4,51.7,50.4,48.2,47.2,46,46,44.8,44,43,41.8,39.7,39.6,5.6,
                9,5.6,11.2,7.8,10.1,5.6,12.4,6.8,5.6,10.1,7.9,5.6,6.7,7.9,10.1,10.1,11.2,10.2,10.1,11.3,
                10.2,6.7,13.5,9.1,10.2,9,10.2,12.4,10.2,13.6,10.1,11.3,13.5,9
        };

        for (int i=0;i<data.length ;i++){
            linearRegression.getInitData().put(Double.parseDouble((i+ 1) + ""), data[i]);
        }

        linearRegression.initData();

        System.out.println("===================");

        LinearRegression linearRegression1 = new LinearRegression();

        double[] data1 = {
//                5.6, 9,5.6,11.2,7.8,
                10.2,9,10.2,12.4,10.2,13.6,10.1,11.3,13.5,9,12.4,13.5,9,10.1,10.1,9.1,9,9,6.8,11.3,10.1,10.2,9,7.9,10.2,14.7,9,10.2,10.1,12.4
//                ,12.4
        };

        for (int i=0;i<data1.length ;i++){
            linearRegression1.getInitData().put(Double.parseDouble((i+ 1) + ""), data1[i]);
        }
        linearRegression1.initData();

//训练集数据
//        linearRegression.getInitData().put(1D, 8D);
//        linearRegression.getInitData().put(1.5D, 9.5D);
//        linearRegression.getInitData().put(2D, 11D);
//        linearRegression.getInitData().put(2.5D, 10D);
//        linearRegression.getInitData().put(3D, 14D);
//根据训练集数据进行线性函数预测
//        linearRegression.initData();


//        System.out.println("intercept: " + intercept);


//        /*
//         * 给定x值，预测y值
//         */
//        System.out.println(linearRegression.getY(130D));
//        /*
//         * 给定y值，预测x值
//         */
//        System.out.println(linearRegression.getX(9.5D));
    }
}
